﻿//
// XN4SL - Xna Framework for Silverlight.
//
using Xn4Sl.Framework;
using System;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Collections.Generic;
using System.Windows.Controls;

namespace Xn4Sl.Framework.Graphics
{
    /// <summary>
    /// Enables a group of sprites to be drawn using the same settings.
    /// </summary>
    public class SpriteBatch : IDisposable
    {
        /// <summary>
        /// The graphics device associated with this SpriteBatch.
        /// </summary>
        private GraphicsDevice graphicsDevice;

        /// <summary>
        /// Sprite transformation matrix.
        /// </summary>
        private Matrix spriteTransformMatrix = Matrix.Identity;

        /// <summary>
        /// Initializes a new instance of the class, which enables a group of sprites
        /// to be drawn using the same settings. 
        /// </summary>
        /// <param name="graphicsDevice">The graphics device where sprites will be drawn.</param>
        public SpriteBatch(GraphicsDevice graphicsDevice) 
        {
            this.graphicsDevice = graphicsDevice;
        }

        /// <summary>
        /// Gets the graphics device associated with this SpriteBatch.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get
            {
                return this.graphicsDevice;
            }
        }

        /// <summary>
        /// Gets a value that indicates whether the object is disposed.
        /// </summary>
        public bool IsDisposed
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Gets or sets the name of this sprite batch.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets an object that uniquely identifies this sprite batch.
        /// </summary>
        public object Tag { get; set; }

        /// <summary>
        /// Occurs when Dispose is called or when this object is finalized and collected
        /// by the garbage collector of the Microsoft .NET common language runtime.
        /// </summary>
        public event EventHandler Disposing;

        /// <summary>
        /// Prepares for drawing sprites.
        /// </summary>
        public void Begin() { }

        /// <summary>
        /// Prepares for drawing sprites with specified blending, sorting, and render 
        /// state options, and a global transform matrix.
        /// </summary>
        /// <param name="blendMode">Blending options to use when rendering.</param>
        /// <param name="sortMode">Sorting options to use when rendering.</param>
        /// <param name="stateMode">Rendering state options.</param>
        /// <param name="transformMatrix">A matrix to apply to position, rotation, 
        /// scale, and depth data passed to SpriteBatch.Draw</param>
        public void Begin(SpriteBlendMode blendMode, SpriteSortMode sortMode, SaveStateMode stateMode, Matrix transformMatrix)
        {
            this.spriteTransformMatrix = transformMatrix;
        }
        
        /// <summary>
        /// Immediately releases the unmanaged resources used by this object.
        /// </summary>
        public void Dispose()
        {
            if (this.Disposing != null) this.Disposing(this, null);
        }

        /// <summary>
        /// Immediately releases the unmanaged resources used by this object.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; 
        /// false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            this.Dispose();
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// destination rectangle, and color tint.
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="destinationRectangle">A rectangle specifying, in screen coordinates, 
        /// where the sprite will be drawn. If this rectangle is not the same size as sourcerectangle, 
        /// the sprite is scaled to fit.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for full color 
        /// with no tinting.</param>
        public void Draw(Texture2D texture, Rectangle destinationRectangle, Color color)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// screen position, and color tint.
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="position">The location, in screen coordinates, where the sprite will be drawn.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for full color with
        /// no tinting.</param>
        public void Draw(Texture2D texture, Vector2 position, Color color)
        {
            //
            // Set position of the texture.
            //
            position.X += this.spriteTransformMatrix.M41;
            position.Y += this.spriteTransformMatrix.M42;

            //
            // Set texture opacity.
            //
            if (color.A < 0xff)
            {
                texture.Opacity = color.A / 255.0;
            }
            else
                texture.Opacity = 1;

            //
            // Set position and visibility of the texture.
            //
            if (texture.Position.X != position.X || texture.Position.Y != position.Y)
            {
                texture.Position = new System.Windows.Point(position.X, position.Y);
            }

            texture.Visible = Visibility.Visible;

            //
            // Set zorder of the texture.
            //
            this.graphicsDevice.Textures.Add(texture);
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// destination and source rectangles, and color tint.
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="destinationRectangle">A rectangle specifying, in screen coordinates,
        /// where the sprite will be drawn. If this rectangle is not the same size as 
        /// sourcerectangle the sprite will be scaled to fit.</param>
        /// <param name="sourceRectangle">A rectangle specifying, in texels, which section 
        /// of the rectangle to draw. Use null to draw the entire texture.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for 
        /// full color with no tinting.</param>
        public void Draw(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle, Color color)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// screen position, source rectangle, and color tint.
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="position">The location, in screen coordinates, where the sprite 
        /// will be drawn.</param>
        /// <param name="sourceRectangle">A rectangle specifying, in texels, which section 
        /// of the rectangle to draw. Use null to draw the entire texture.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for 
        /// full color with no tinting.</param>
        public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// destination, and source rectangles, color tint, rotation, origin, effects,
        /// and sort depth. 
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="destinationRectangle">A rectangle specifying, in screen coordinates, 
        /// where the sprite will be drawn. If this rectangle is not the same size as sourceRectangle, 
        /// the sprite is scaled to fit.</param>
        /// <param name="sourceRectangle">A rectangle specifying, in texels, which section 
        /// of the rectangle to draw. Use null to draw the entire texture.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for 
        /// full color with no tinting.</param>
        /// <param name="rotation">The angle, in radians, to rotate the sprite around the origin.</param>
        /// <param name="origin">The origin of the sprite. Specify (0,0) for the upper-left corner.</param>
        /// <param name="effects">Rotations to apply prior to rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front) 
        /// and 1 (back).You must specify either SpriteSortMode.FrontToBack or 
        /// SpriteSortMode.BackToFront for this parameter to affect sprite drawing.</param>
        public void Draw(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle, Color color, 
                         float rotation, Vector2 origin, SpriteEffects effects, float layerDepth)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// screen position, optional source rectangle, color tint, rotation, origin,
        /// scale, effects, and sort depth.
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="position">The location, in screen coordinates, where the sprite will be drawn.</param>
        /// <param name="sourceRectangle">A rectangle specifying, in texels, which section 
        /// of the rectangle to draw. Use null to draw the entire texture.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for full color with
        /// no tinting.</param>
        /// <param name="rotation">The angle, in radians, to rotate the sprite around the origin.</param>
        /// <param name="origin">The origin of the sprite. Specify (0,0) for the upper-left corner.</param>
        /// <param name="scale">Uniform multiple by which to scale the sprite width and height.</param>
        /// <param name="effects"> Rotations to apply prior to rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front) and 1 (back).
        /// You must specify either SpriteSortMode.FrontToBack or SpriteSortMode.BackToFront for this 
        /// parameter to affect sprite drawing.</param>
        public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, 
                         Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
        {
            //
            // Set new position of the texture.
            //
            Vector2 pos = position - origin;

            //
            // Set new origin of the texture.
            //
            texture.Origin = new System.Windows.Point(origin.X, origin.Y);

            //
            // Check if section is selected.
            //
            if (sourceRectangle.HasValue)
            {
                // Clip specified texture section.
                texture.Clip = sourceRectangle.Value;
                // Adjust texture position.
                pos.X = pos.X - sourceRectangle.Value.X;
                // Adjust the texture origin.
                texture.Origin = new System.Windows.Point(origin.X + sourceRectangle.Value.X, origin.Y);
            }

            //
            // Calculate horizontal and vertical flip factors.
            //
            int sx = (effects & SpriteEffects.FlipHorizontally) > 0 ? -1 : 1;
            int sy = (effects & SpriteEffects.FlipVertically) > 0 ? -1 : 1;
            //
            // Flip and scale.
            //
            texture.Scale = new System.Windows.Point(sx * scale, sy * scale);

            //
            // Draw texture.
            //
            this.Draw(texture, pos, color);
        }

        /// <summary>
        /// Adds a sprite to the batch of sprites to be rendered, specifying the texture,
        /// screen position, source rectangle, color tint, rotation, origin, scale, effects,
        /// and sort depth.
        /// </summary>
        /// <param name="texture">The sprite texture.</param>
        /// <param name="position">The location, in screen coordinates, where the sprite will be drawn.</param>
        /// <param name="sourceRectangle">A rectangle specifying, in texels, which 
        /// section of the rectangle to draw. Use null to draw the entire texture.</param>
        /// <param name="color">The color channel modulation to use. Use Color.White for 
        /// full color with no tinting.</param>
        /// <param name="rotation">The angle, in radians, to rotate the sprite around the origin.</param>
        /// <param name="origin">The origin of the sprite. Specify (0,0) for the upper-left corner.</param>
        /// <param name="scale">Vector containing separate scalar multiples for 
        /// the x- and y-axes of the sprite.</param>
        /// <param name="effects">Rotations to apply before rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front)
        /// and 1 (back).You must specify either SpriteSortMode.FrontToBack or 
        /// SpriteSortMode.BackToFront for this parameter to affect sprite drawing.</param>
        public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, 
                         Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Adds a sprite string to the batch of sprites to be rendered, specifying the
        /// font, output text, screen position, and color tint.
        /// </summary>
        /// <param name="spriteFont">The sprite font.</param>
        /// <param name="text">The string to draw.</param>
        /// <param name="position">The location, in screen coordinates, where the text will be drawn.</param>
        /// <param name="color">The desired color of the text.</param>
        public void DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color)
        {
            //
            // Set string position
            //
            position.X += this.spriteTransformMatrix.M41;
            position.Y += this.spriteTransformMatrix.M42;

            //
            // Get unused or new text block from graphic device.
            //
            TextBlock textBlock = this.graphicsDevice.Text;

            //
            // Set text block properties.
            //
            textBlock.Text = text;
            textBlock.Foreground = new SolidColorBrush(color.ToColor());
            textBlock.SetValue(Canvas.LeftProperty, (double)position.X);
            textBlock.SetValue(Canvas.TopProperty, (double)position.Y);
            textBlock.FontSize = spriteFont.FontSize;
            textBlock.FontSource = spriteFont.FontSource;
            textBlock.FontFamily = spriteFont.FontFamily;
            textBlock.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// Adds a mutable sprite string to the batch of sprites to be rendered, specifying
        /// the font, output text, screen position, and color tint.
        /// </summary>
        /// <param name="spriteFont">The sprite font.</param>
        /// <param name="text">The mutable (read/write) string to draw.</param>
        /// <param name="position">The location, in screen coordinates, where the text will be drawn.</param>
        /// <param name="color">The desired color of the text.</param>
        public void DrawString(SpriteFont spriteFont, StringBuilder text, Vector2 position, Color color)
        {
            this.DrawString(spriteFont, text.ToString(), position, color);
        }

        /// <summary>
        /// Adds a sprite string to the batch of sprites to be rendered, specifying the
        /// font, output text, screen position, color tint, rotation, origin, scale,
        /// and effects.
        /// </summary>
        /// <param name="spriteFont">The sprite font.</param>
        /// <param name="text">The string to draw.</param>
        /// <param name="position">The location, in screen coordinates, where the text will be drawn.</param>
        /// <param name="color">The desired color of the text.</param>
        /// <param name="rotation">The angle, in radians, to rotate the text around the origin.</param>
        /// <param name="origin">The origin of the string. Specify (0,0) for the upper-left corner.</param>
        /// <param name="scale">Uniform multiple by which to scale the sprite width and height.</param>
        /// <param name="effects">Rotations to apply prior to rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front) and 1 (back).</param>
        public void DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
        {
            this.DrawString(spriteFont, text, position, color, rotation, origin, new Vector2(scale), effects, layerDepth);
        }

        /// <summary>
        /// Adds a sprite string to the batch of sprites to be rendered, specifying the
        /// font, output text, screen position, color tint, rotation, origin, scale,
        /// effects, and depth.
        /// </summary>
        /// <param name="spriteFont">The sprite font.</param>
        /// <param name="text">The string to draw.</param>
        /// <param name="position">The location, in screen coordinates, where the text will be drawn.</param>
        /// <param name="color">The desired color of the text.</param>
        /// <param name="rotation">The angle, in radians, to rotate the text around the origin.</param>
        /// <param name="origin">The origin of the string. Specify (0,0) for the upper-left corner.</param>
        /// <param name="scale">Vector containing separate scalar multiples for the x- and y-axes of the sprite.</param>
        /// <param name="effects">Rotations to apply prior to rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front) and 1 (back).</param>
        public void DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
        {
            if (rotation + origin.X + origin.Y + layerDepth != 0 || 
                scale.X != 1 || scale.Y != 1 ||
                effects != SpriteEffects.None)
            {
                throw new NotImplementedException();
            }

            this.DrawString(spriteFont, text, position, color);
        }

        /// <summary>
        /// Adds a mutable sprite string to the batch of sprites to be rendered, specifying
        /// the font, output text, screen position, color tint, rotation, origin, scale,
        /// and effects.
        /// </summary>
        /// <param name="spriteFont">The sprite font.</param>
        /// <param name="text">The mutable (read/write) string to draw.</param>
        /// <param name="position">The location, in screen coordinates, where the text will be drawn.</param>
        /// <param name="color">The desired color of the text.</param>
        /// <param name="rotation">The angle, in radians, to rotate the text around the origin.</param>
        /// <param name="origin">The origin of the string. Specify (0,0) for the upper-left corner.</param>
        /// <param name="scale">Uniform multiple by which to scale the sprite width and height.</param>
        /// <param name="effects">Rotations to apply prior to rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front) and 1 (back).</param>
        public void DrawString(SpriteFont spriteFont, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
        {
            this.DrawString(spriteFont, text.ToString(), position, color, rotation, origin, new Vector2(scale), effects, layerDepth);
        }

        /// <summary>
        /// Adds a mutable sprite string to the batch of sprites to be rendered, specifying
        /// the font, output text, screen position, color tint, rotation, origin, scale,
        /// effects, and depth. 
        /// </summary>
        /// <param name="spriteFont">The sprite font.</param>
        /// <param name="text">The mutable (read/write) string to draw.</param>
        /// <param name="position">The location, in screen coordinates, where the text will be drawn.</param>
        /// <param name="color">The desired color of the text.</param>
        /// <param name="rotation">The angle, in radians, to rotate the text around the origin.</param>
        /// <param name="origin">The origin of the string. Specify (0,0) for the upper-left corner.</param>
        /// <param name="scale">Vector containing separate scalar multiples for the x- and y-axes of the sprite.</param>
        /// <param name="effects">Rotations to apply prior to rendering.</param>
        /// <param name="layerDepth">The sorting depth of the sprite, between 0 (front) and 1 (back).</param>
        public void DrawString(SpriteFont spriteFont, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
        {
            this.DrawString(spriteFont, text.ToString(), position, color, rotation, origin, scale, effects, layerDepth);
        }

        /// <summary>
        /// End drawing sprites.
        /// </summary>
        public void End()
        {
            this.spriteTransformMatrix = Matrix.Identity;
        }
    }
}

